#include <iostream>
#include <unistd.h>
#include <memory>
#include <assert.h>
#include <cstring>
#include <fcntl.h>
#include <sys/socket.h>

#include "socket_worker.h"
#include "sunnet.h"

void socket_worker::init()
{
    cout << "socket worker init" << endl;
    // 创建 epoll 对象
    this->epollfd = epoll_create(1024); // 参数实际上不再具有实际意义, 大于 0 即可
    assert(this->epollfd > 0);
}

void socket_worker::operator()()
{
    while (true)
    {
        const int EVENT_SIZE = 64;
        struct epoll_event events[EVENT_SIZE];
        /**
         * 会阻塞线程, 别的线程会使用 epoll_ctl 方法往 epoll 中增加监听的 fd.
         * epoll_ctl 和 epoll_wait 会同时操作 fd 对应的 epoll_event 数据结构
         * 按照参考书, 说是由操作系统保证线程安全.
         * 
         * 网上搜索的结论是: 不是 100% 安全的, 实际应用应该使用 skynet 的处理方式, 都由网络线程处理.
         */
        int event_count = epoll_wait(this->epollfd, events, EVENT_SIZE, -1);

        // 取得事件
        for (int i = 0; i < event_count; i++)
        {
            epoll_event ev = events[i];
            onevent(ev);
        }
    }
}

void socket_worker::add_event(int fd)
{
    struct epoll_event ev;
    ev.events = EPOLLIN | EPOLLET;
    ev.data.fd = fd;

    if (epoll_ctl(this->epollfd, EPOLL_CTL_ADD, fd, &ev) == -1)
    {
        cout << "add_event epoll_ctl fail: " << strerror(errno) << endl;
    }
}

void socket_worker::remove_event(int fd)
{
    cout << "RemoveEvent fd " << fd << endl;
    epoll_ctl(this->epollfd, EPOLL_CTL_DEL, fd, NULL);
}

void socket_worker::modify_event(int fd, bool epollout)
{
    cout << "ModifyEvent fd " << fd << " " << epollout << endl;
    struct epoll_event ev;
    ev.data.fd = fd;

    if (epollout)
    {
        ev.events = EPOLLIN | EPOLLOUT | EPOLLET;
    }
    else
    {
        ev.events = EPOLLIN | EPOLLET;
    }

    epoll_ctl(this->epollfd, EPOLL_CTL_MOD, fd, &ev);
}

void socket_worker::onevent(struct epoll_event ev)
{
    // cout << "on event" << ev.data.fd << endl;
    int fd = ev.data.fd;
    auto conn = sunnet::inst->get_conn(fd);
    if (!conn)
    {
        cout << "onevent error, conn is nullptr, fd is " << fd << endl;
        return;
    }
    // event type
    bool isread = ev.events & EPOLLIN;
    bool iswrite = ev.events & EPOLLOUT;
    bool iserror = ev.events & EPOLLERR;

    // listen socket
    if (conn->type == conn_type::listen)
    {
        if (isread)
            onaccept(conn);
    }
    else
    {
        if (isread || iswrite)
            onrw(conn, isread, iswrite);
        if (iserror)
            cout << "onerror, fd " << conn->fd << endl;
    }
}

void socket_worker::onaccept(shared_ptr<conn> conninst)
{
    cout << "onaccept, fd: " << conninst->fd << endl;
    // 1. accept
    int clientfd = accept(conninst->fd, NULL, NULL);
    if (clientfd < 0)
        cout << "accpet error" << endl;
    // 2. set non-blocking
    fcntl(clientfd, F_SETFL, O_NONBLOCK);
    // 3. 添加连接对象
    sunnet::inst->add_conn(clientfd, conninst->serviceid, conn_type::client); // serviceid 业务层的 id
    // 4. 添加到 epoll
    struct epoll_event ev;
    ev.events = EPOLLIN | EPOLLET;
    ev.data.fd = clientfd;
    if (epoll_ctl(this->epollfd, EPOLL_CTL_ADD, clientfd, &ev) == -1)
        cout << "onaccept epoll_ctl fail: " << strerror(errno) << endl;
    // 5. 通知服务, 让业务层(actor) 处理
    auto msg = make_shared<socket_accept_msg>();
    msg->type = base_msg::TYPE::socket_accept;
    msg->listen_fd = conninst->fd;
    msg->client_fd = clientfd;

    sunnet::inst->send(conninst->serviceid, msg);
}

void socket_worker::onrw(shared_ptr<conn> conninst, bool r, bool w)
{
    /**
     * 只是发出通知, 并不处理, 让 Actor 来处理消息
     * 与 skynet 的设计不一样.
     *
     * skynet 会读完消息, 然后发送到对应的 Actor
     */
    cout << "onrw fd: " << conninst->fd << endl;
    auto msg = make_shared<socket_rw>();
    msg->type = base_msg::TYPE::socket_rw;
    msg->fd = conninst->fd;
    msg->is_read = r;
    msg->is_write = w;

    sunnet::inst->send(conninst->serviceid, msg);
}